import 'dart:async';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class KqSwiper<T extends IShowData> extends StatefulWidget {
  /// 展示的数据
  final List<T> showData;

  /// 点击回调
  final Function(int index, T data)? onTap;

  /// 界面宽度
  final double width;

  /// 界面高度
  final double height;

  /// 是否开启自动滑动
  final bool isAutoSlide;

  ///指示器
  final IIndicator? indicator;

  const KqSwiper(
      {super.key,
      required this.showData,
      this.onTap,
      this.width = 360,
      this.height = 250,
      this.isAutoSlide = true,
      this.indicator});

  @override
  State<StatefulWidget> createState() => _KqSwiperState<T>();
}

class _KqSwiperState<T extends IShowData> extends State<KqSwiper<T>> {
  /// 执行自动滑动的时间间隔s
  static const int slideTime = 6;

  /// 图片数据
  final List<ui.Image> images = [];

  /// 当前正在显示的图片
  int index = 0;

  /// 前一张图片
  ui.Image? _preImage;

  /// 当前图片
  ui.Image? _curImage;

  /// 下一张图片
  ui.Image? _nextImage;

  /// 滑动偏移
  double _offset = 0;

  /// 执行滑动动画计时器
  Timer? _timer;

  ///定时滑动计时器
  Timer? _timer2;

  /// 指示器
  IIndicator? _indicator;

  /// 当Indicator的类型为widget时的widget列表值
  List<Widget>? _indicatorWidgetList;

  /// 是否向左滑动
  bool _isLeftSwiper = true;

  /// 是否正在滑动
  bool _isSliding = false;

  @override
  void initState() {
    _initImage();
    _indicator = widget.indicator ?? DrawIndicator();
    super.initState();
  }

  void _initImage() async {
    for (T data in widget.showData) {
      images.add(await getImage(data,
          width: widget.width.toInt(), height: widget.height.toInt()));
    }
    _showImage();
    _startSlide();
  }

  void _showImage() {
    if (index == 0) {
      _preImage = images[widget.showData.length - 1];
    } else {
      _preImage = images[index - 1];
    }
    _curImage = images[index];
    if (index == widget.showData.length - 1) {
      _nextImage = images[0];
    } else {
      _nextImage = images[index + 1];
    }
    _initWidgetIndicator();
    setState(() {});
  }

  Future<ui.Image> getImage(T data, {width, height}) async {
    ui.Image image;
    if (data.type == FileSourceType.network) {
      image = await getNetImage(data.path, width: width, height: height);
    } else if (data.type == FileSourceType.asset) {
      image = await getAssetImage(data.path, width: width, height: height);
    } else {
      image = await getNetImage(data.path, width: width, height: height);
    }
    return image;
  }

  void _animateTo(bool isSwiper, double hasSwiperWidth) {
    if (!_isSliding) {
      _initWidgetIndicator();
      double showCount = 0;
      if (isSwiper) {
        showCount = widget.width - hasSwiperWidth;
      } else {
        showCount = hasSwiperWidth;
      }
      showCount = showCount / 1.2;
      //计时器，每1毫秒执行一次
      const period = Duration(milliseconds: 1);
      _timer = Timer.periodic(period, (timer) {
        showCount--;
        if (_offset > 0) {
          //右滑
          if (_offset < widget.width) {
            if (isSwiper) {
              _offset++;
            } else {
              _offset--;
            }
          } else {
            showCount = 0;
          }
        } else {
          //左滑
          if (_offset > -widget.width) {
            if (isSwiper) {
              _offset--;
            } else {
              _offset++;
            }
          } else {
            showCount = 0;
          }
        }
        //计时器结束条件
        if (showCount.toInt() == 0) {
          timer.cancel();
          _offset = 0;
          _showImage();
          _startSlide();
        }
        //只有当widget状态为mounted时才执行setState防止内存泄露
        if (mounted) {
          setState(() {});
        }
      });
    }
  }

  void _initWidgetIndicator() {
    if (_indicator?.type == IndicatorType.widget) {
      _indicatorWidgetList =
          _indicator?.onIWidget(widget.showData.length, index);
    }
  }

  void _startSlide() {
    if (widget.isAutoSlide) {
      if (_timer2 == null || !_timer2!.isActive) {
        //计时器，每1毫秒执行一次
        const period = Duration(seconds: slideTime);
        _timer2 = Timer.periodic(period, (timer) {
          if (index == widget.showData.length - 1) {
            index = 0;
          } else {
            index++;
          }
          _animateTo(true, 0);
        });
      }
    }
  }

  @override
  void dispose() {
    //退出时关闭计时器防止内存泄露
    _timer?.cancel();
    _timer2?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: widget.height,
      width: widget.width,
      child: RepaintBoundary(
          child: Stack(
              alignment: Alignment.center,
              fit: StackFit.expand,
              children: [
            GestureDetector(
              child: ClipRect(
                  child: CustomPaint(
                painter: _SwiperPinter(this, widget.showData.length, index,
                    _preImage, _curImage, _nextImage, _offset, _indicator!),
              )),
              onTap: () {
                widget.onTap?.call(index, widget.showData[index]);
              },
              onHorizontalDragDown: (DragDownDetails details) {
                _timer2?.cancel();
                _isSliding = false;
              },
              onHorizontalDragUpdate: (DragUpdateDetails details) {
                _offset += details.delta.dx;
                if (details.delta.dx > 0 && _offset >= widget.width) {
                  _offset = widget.width;
                } else if (details.delta.dx < 0 && _offset <= -widget.width) {
                  _offset = -widget.width;
                }
                _isLeftSwiper = details.delta.dx < 0;
                _isSliding = true;
                setState(() {});
              },
              onHorizontalDragEnd: (DragEndDetails details) {
                bool isSwiper = false;
                _isSliding = false;
                if ((details.primaryVelocity != null &&
                        details.primaryVelocity!.abs() <= 1000 &&
                        _offset.abs() >= widget.width / 3) ||
                    (details.primaryVelocity != null &&
                        details.primaryVelocity!.abs() > 1000)) {
                  if (_isLeftSwiper) {
                    if (index == widget.showData.length - 1) {
                      index = 0;
                    } else {
                      index++;
                    }
                  } else {
                    if (index == 0) {
                      index = widget.showData.length - 1;
                    } else {
                      index--;
                    }
                  }
                  isSwiper = true;
                }
                _animateTo(isSwiper, _offset.abs());
              },
            ),
            Positioned(
                bottom: 10,
                child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: _indicatorWidgetList ?? [])),
          ])),
    );
  }
}

class _SwiperPinter extends CustomPainter {
  final _KqSwiperState state;
  final int _length;
  final int _index;
  final ui.Image? _preImage;
  final ui.Image? _curImage;
  final ui.Image? _nextImage;
  final double _offset;
  final IIndicator _indicator;

  Paint selfPaint = Paint()
    ..color = Colors.blue
    ..style = PaintingStyle.fill
    ..isAntiAlias = true
    ..strokeCap = StrokeCap.butt
    ..strokeWidth = 30.0;

  _SwiperPinter(this.state, this._length, this._index, this._preImage,
      this._curImage, this._nextImage, this._offset, this._indicator);

  @override
  void paint(Canvas canvas, Size size) {
    if (_preImage == null || _curImage == null || _nextImage == null) {
      return;
    }
    canvas.drawImage(
        _preImage!, Offset(-state.widget.width + _offset, 0), selfPaint);
    canvas.drawImage(_curImage!, Offset(0 + _offset, 0), selfPaint);
    canvas.drawImage(
        _nextImage!, Offset(state.widget.width + _offset, 0), selfPaint);

    if (_indicator.type == IndicatorType.draw) {
      _indicator.onIDraw(
          canvas, _length, _index, state.widget.width, state.widget.height);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

/// 文件源类型
enum FileSourceType {
  /// 网络文件
  network,

  /// 系统文件
  asset
}

/// 文件格式
abstract class IShowData {
  /// 展示的图片路径
  final String path;

  /// 图片路径位置
  final FileSourceType type;

  IShowData(this.path, this.type);
}

//获取网络图片 返回ui.Image
Future<ui.Image> getNetImage(String url, {width, height}) async {
  ByteData data = await NetworkAssetBundle(Uri.parse(url)).load(url);
  ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),
      targetWidth: width, targetHeight: height);
  ui.FrameInfo fi = await codec.getNextFrame();
  return fi.image;
}

//获取本地图片，返回ui.Image
Future<ui.Image> getAssetImage(String asset, {width, height}) async {
  ByteData data = await rootBundle.load(asset);
  ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),
      targetWidth: width, targetHeight: height);
  ui.FrameInfo fi = await codec.getNextFrame();
  return fi.image;
}

enum IndicatorType {
  /// widget类型
  widget,

  /// 绘制类型
  draw
}

/// 指示器，实现该类进行自定义绘制
abstract class IIndicator {
  final IndicatorType type;

  IIndicator(this.type);

  /// 自定义绘制
  /// length 指示器个数
  /// curIndex 指示器当前位置
  /// w 控件宽度
  /// h 控件高度
  onIDraw(Canvas canvas, int length, int curIndex, double w, double h);

  /// 自定义传widget
  /// length 指示器个数
  /// curIndex 指示器当前位置
  /// return List<Widget> 指示器列表
  List<Widget> onIWidget(int length, int curIndex);
}

/// 传widget的指示器抽象类，传widget的方式需要继承该类实现
abstract class IWidgetIndicator extends IIndicator {
  IWidgetIndicator() : super(IndicatorType.widget);

  @override
  onIDraw(Canvas canvas, int length, int curIndex, double w, double h) {
    ///啥也不干
  }

  @override
  List<Widget> onIWidget(int length, int curIndex) {
    return onWidget(length, curIndex);
  }

  /// 过渡方法
  List<Widget> onWidget(int length, int curIndex);
}

/// 自定义绘制的指示器抽象类，自定义绘制的方式需要继承该类实现
abstract class IDrawIndicator extends IIndicator {
  IDrawIndicator() : super(IndicatorType.draw);

  @override
  onIDraw(Canvas canvas, int length, int curIndex, double w, double h) {
    onDraw(canvas, length, curIndex, w, h);
  }

  @override
  List<Widget> onIWidget(int length, int curIndex) {
    ///啥也不干
    return [];
  }

  /// 过渡方法
  onDraw(Canvas canvas, int length, int curIndex, double w, double h);
}

/// 内置绘制指示器
class DrawIndicator extends IDrawIndicator {
  /// 指示器单个点x方向偏移
  final double _dotOffsetDx = 10;

  /// 指示器单个点y方向偏移
  final double _dotOffsetDy = 10;

  /// 指示器点的半径
  final double _dotRadius = 4;

  /// 指示器画笔
  final Paint _paint = Paint()
    ..style = PaintingStyle.fill
    ..isAntiAlias = true
    ..strokeCap = StrokeCap.butt
    ..strokeWidth = 30.0;

  @override
  onDraw(Canvas canvas, int length, int curIndex, double w, double h) {
    for (int i = 0; i < length; i++) {
      //绘制当前指示器
      if (i == curIndex) {
        _paint.color = Colors.blue;
        canvas.drawCircle(
            Offset(
                (w / 2 - (_dotRadius * 2 + (length - 1) * _dotOffsetDx) / 2) +
                    (_dotRadius * 2 + _dotOffsetDx) * i,
                h - _dotOffsetDy),
            _dotRadius,
            _paint);
        // 绘制非当前的其它指示器
      } else {
        _paint.color = Colors.grey;
        canvas.drawCircle(
            Offset(
                (w / 2 - (_dotRadius * 2 + (length - 1) * _dotOffsetDx) / 2) +
                    (_dotRadius * 2 + _dotOffsetDx) * i,
                h - _dotOffsetDy),
            _dotRadius,
            _paint);
      }
    }
  }
}

/// 内置widget指示器
class WidgetIndicator extends IWidgetIndicator {
  @override
  List<Widget> onWidget(int length, int curIndex) {
    List<Widget> widgets = [];
    for (int i = 0; i < length; i++) {
      if (curIndex == i) {
        widgets.add(Text(
          (i + 1).toString(),
          style:
              const TextStyle(color: Colors.red, backgroundColor: Colors.white),
        ));
      } else {
        widgets.add(Text(
          (i + 1).toString(),
          style: const TextStyle(
              color: Colors.black, backgroundColor: Colors.white),
        ));
      }
    }
    return widgets;
  }
}
